ImapFolderAgent: Enable notification of mails already marked as read.

Add a condition key "is_unread" to allow user to select mails based on
the read status.

Akinori MUSHA 10 years ago
parent
commit
d144d3797d
1 changed files with 61 additions and 22 deletions
  1. 61 22
      app/models/agents/imap_folder_agent.rb

+ 61 - 22
app/models/agents/imap_folder_agent.rb

@@ -11,7 +11,9 @@ module Agents
11 11
     description <<-MD
12 12
 
13 13
       The ImapFolderAgent checks an IMAP server in specified folders
14
-      and creates Events based on new unread mails.
14
+      and creates Events based on new mails found since the last run.
15
+      In the first visit to a foler, this agent only checks for the
16
+      initial status and does not create events.
15 17
 
16 18
       Specify an IMAP server to connect with `host`, and set `ssl` to
17 19
       true if the server supports IMAP over SSL.  Specify `port` if
@@ -65,6 +67,13 @@ module Agents
65 67
           body.  The default value is `['text/plain', 'text/enriched',
66 68
           'text/html']`.
67 69
 
70
+      - "is_unread"
71
+
72
+          Setting this to true or false means only mails that is
73
+          marked as unread or read respectively, are selected.
74
+
75
+          If this key is unspecified or set to null, it is ignored.
76
+
68 77
       - "has_attachment"
69 78
 
70 79
           Setting this to true or false means only mails that does or does
@@ -77,11 +86,13 @@ module Agents
77 86
       Each agent instance memorizes the highest UID of mails that are
78 87
       found in the last run for each watched folder, so even if you
79 88
       change a set of conditions so that it matches mails that are
80
-      missed previously, or if you unmark already seen mails as read,
81
-      they will not show up as new events.  Also, in order to avoid
82
-      duplicated notification it keeps a list of Message-Id's of 100
83
-      most recent mails, so if multiple mails of the same Message-Id
84
-      are found, you will only see one event out of them.
89
+      missed previously, or if you alter the flag status of already
90
+      found mails, they will not show up as new events.
91
+
92
+      Also, in order to avoid duplicated notification it keeps a list
93
+      of Message-Id's of 100 most recent mails, so if multiple mails
94
+      of the same Message-Id are found, you will only see one event
95
+      out of them.
85 96
     MD
86 97
 
87 98
     event_description <<-MD
@@ -200,7 +211,7 @@ module Agents
200 211
                 errors.add(:base, 'conditions.%s contains a non-string object' % key)
201 212
               end
202 213
             }
203
-          when 'has_attachment'
214
+          when 'is_unread', 'has_attachment'
204 215
             case boolify(value)
205 216
             when true, false
206 217
             else
@@ -258,6 +269,8 @@ module Agents
258 269
             }
259 270
           when 'has_attachment'
260 271
             boolify(value) == mail.has_attachment?
272
+          when 'is_unread'
273
+            true  # already filtered out by each_unread_mail
261 274
           else
262 275
             log 'Unknown condition key ignored: %s' % key
263 276
             true
@@ -321,25 +334,47 @@ module Agents
321 334
           imap.select(folder)
322 335
           uidvalidity = imap.uidvalidity
323 336
 
324
-          if lastseenuid = lastseen[uidvalidity]
325
-            seen[uidvalidity] = lastseenuid
326
-            uids = imap.uid_fetch((lastseenuid + 1)..-1, 'FLAGS').
327
-                   each_with_object([]) { |data, ret|
328
-              uid, flags = data.attr.values_at('UID', 'FLAGS')
329
-              seen[uidvalidity] = uid
330
-              next if uid <= lastseenuid || flags.include?(:Seen)
331
-              ret << uid
332
-            }
333
-          else
334
-            uids = imap.uid_search('UNSEEN')
335
-            seen[uidvalidity] = uids.max unless uids.empty?
336
-          end
337
+          lastseenuid = lastseen[uidvalidity]
338
+
339
+          if lastseenuid.nil?
340
+            maxseq = imap.responses['EXISTS'].last
341
+
342
+            log "Recording the initial status: %s" % pluralize(maxseq, 'existing mail')
343
+
344
+            if maxseq > 0
345
+              seen[uidvalidity] = imap.fetch(maxseq, 'UID').last.attr['UID']
346
+            end
337 347
 
338
-          if uids.empty?
339
-            log "No unread mails"
340 348
             next
341 349
           end
342 350
 
351
+          seen[uidvalidity] = lastseenuid
352
+          is_unread = boolify(interpolated['conditions']['is_unread'])
353
+
354
+          uids = imap.uid_fetch((lastseenuid + 1)..-1, 'FLAGS').
355
+                 each_with_object([]) { |data, ret|
356
+            uid, flags = data.attr.values_at('UID', 'FLAGS')
357
+            seen[uidvalidity] = uid
358
+            next if uid <= lastseenuid
359
+
360
+            case is_unread
361
+            when nil, !flags.include?(:Seen)
362
+              ret << uid
363
+            end
364
+          }
365
+
366
+          log pluralize(uids.size,
367
+                        case is_unread
368
+                        when true
369
+                          'new unread mail'
370
+                        when false
371
+                          'new read mail'
372
+                        else
373
+                          'new mail'
374
+                        end)
375
+
376
+          next if uids.empty?
377
+
343 378
           imap.uid_fetch_mails(uids).each { |mail|
344 379
             yield mail, notified
345 380
           }
@@ -391,6 +426,10 @@ module Agents
391 426
       File.fnmatch?(pattern, value, FNM_FLAGS)
392 427
     end
393 428
 
429
+    def pluralize(count, noun)
430
+      "%d %s" % [count, noun.pluralize(count)]
431
+    end
432
+
394 433
     class Client < ::Net::IMAP
395 434
       class << self
396 435
         def open(host, port, ssl)